<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/SDWebImage1/">
<meta property="og:title" content="       SDWebImage Source Probe: WebCache | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       SDWebImage Source Probe: WebCache | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/SDWebImage1/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>SDWebImage Source Probe: WebCache</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-09-04"> 2016-09-04
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <p>最近两天，在完成工作业务之余，除了看书，自己也要开始深入的阅读经典的源码。来完善我的 <a href="https://desgard.com/iOS-Source-Probe/">iOS 源码探求</a> 系列文章。</p>
<p>对源码的阅读是一个长久的学习过程，我会将业务中最常用的一些经典三方库拿出来进行学习。这一点我很敬佩 <a href="http://draveness.me/">@Draveness</a> 的精神，并向他看齐。</p>
<h2 id="sdwebimage-">SDWebImage 简单介绍</h2>
<p><a href="https://github.com/rs/SDWebImage">SDWebImage</a> 根据官方文档，其实就是提供了以下功能：</p>
<blockquote>
  <p>Asynchronous image downloader with cache support with an UIImageView category.</p>
</blockquote>
<p>一个异步下载图片并且带有 <code>UIImageView</code> Category 的缓存库。其好用的原因还在于其简介的接口。话不多说，开始主要内容。本系列文章使用的 SDWebImage 版本为 <code>v3.8.1</code>。</p>
<h2 id="section">多重入口委托构造器</h2>
<p>在使用 SD 库的时候，最常调用的方法如下：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">[</span><span class="nb">self</span><span class="o">.</span><span class="n">imageView</span> <span class="ss">sd_setImageWithURL</span><span class="p">:</span><span class="o">[</span><span class="no">NSURL</span> <span class="ss">URLWithString</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;url&quot;</span><span class="o">]</span> 
				<span class="ss">placeholderImage</span><span class="p">:</span><span class="o">[</span><span class="no">UIImage</span> <span class="ss">imageNamed</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;placeholder.png&quot;</span><span class="o">]]</span><span class="p">;</span></code></pre></div>
<p>由此，对 <code>UIImageView</code> 的图片一部加载完成了。进入到该方法内部，在其 <code>.h</code> 的文件中看到以下接口：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">sd_setImageWithURL</span><span class="p">:(</span><span class="no">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">url</span><span class="p">;</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">sd_setImageWithURL</span><span class="p">:(</span><span class="no">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">url</span> <span class="ss">placeholderImage</span><span class="p">:(</span><span class="no">UIImage</span> <span class="o">*</span><span class="p">)</span><span class="n">placeholder</span><span class="p">;</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">sd_setImageWithURL</span><span class="p">:(</span><span class="no">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">url</span> <span class="ss">placeholderImage</span><span class="p">:(</span><span class="no">UIImage</span> <span class="o">*</span><span class="p">)</span><span class="n">placeholder</span> <span class="ss">options</span><span class="p">:(</span><span class="no">SDWebImageOptions</span><span class="p">)</span><span class="n">options</span><span class="p">;</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">sd_setImageWithURL</span><span class="p">:(</span><span class="no">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">url</span> <span class="ss">placeholderImage</span><span class="p">:(</span><span class="no">UIImage</span> <span class="o">*</span><span class="p">)</span><span class="n">placeholder</span> <span class="ss">options</span><span class="p">:(</span><span class="no">SDWebImageOptions</span><span class="p">)</span><span class="n">options</span> <span class="ss">progress</span><span class="p">:(</span><span class="no">SDWebImageDownloaderProgressBlock</span><span class="p">)</span><span class="n">progressBlock</span> <span class="ss">completed</span><span class="p">:(</span><span class="no">SDWebImageCompletionBlock</span><span class="p">)</span><span class="n">completedBlock</span><span class="p">;</span></code></pre></div>
<p>作为 SD 的入口函数，在 sd_setImageWithURL 方法中采用了多种参数灵活搭配的同名方法。而内部实质，都在向最后一个 sd_setImageWithURL 传入参数最多的方法进行调用处理。</p>
<p>在 c++ 0x 中，这种方式被广泛的使用在系统库的 class 中作为类的委托构造器（Delegate Constructor）。这样做的好处是，<strong>可以清晰的梳理函数构造逻辑，减轻代码编写量</strong>。</p>
<h2 id="setimagewithurl-">setImageWithURL 处理流程</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="err">委托构造器最高级入口</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">sd_setImageWithURL</span><span class="p">:(</span><span class="no">NSURL</span> <span class="o">*</span><span class="p">)</span><span class="n">url</span> <span class="ss">placeholderImage</span><span class="p">:(</span><span class="no">UIImage</span> <span class="o">*</span><span class="p">)</span><span class="n">placeholder</span> <span class="ss">options</span><span class="p">:(</span><span class="no">SDWebImageOptions</span><span class="p">)</span><span class="n">options</span> <span class="ss">progress</span><span class="p">:(</span><span class="no">SDWebImageDownloaderProgressBlock</span><span class="p">)</span><span class="n">progressBlock</span> <span class="ss">completed</span><span class="p">:(</span><span class="no">SDWebImageCompletionBlock</span><span class="p">)</span><span class="n">completedBlock</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">【要点</span> <span class="mi">1</span><span class="err">】取消该</span> <span class="no">UIImageView</span> <span class="err">的下载队列</span>
    <span class="o">[</span><span class="nb">self</span> <span class="n">sd_cancelCurrentImageLoad</span><span class="o">]</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">【要点</span> <span class="mi">2</span><span class="err">】对应的</span> <span class="no">UIImageView</span> <span class="err">增加一个对应的</span> <span class="n">url</span> <span class="err">属性</span>
    <span class="n">objc_setAssociatedObject</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">imageURLKey</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="no">OBJC_ASSOCIATION_RETAIN_NONATOMIC</span><span class="p">);</span>
	<span class="sr">//</span> <span class="err">根据参数条件，是一个</span> <span class="n">optional</span> <span class="err">执行块</span>
	<span class="sr">//</span> <span class="err">在未发送请求过去网络图片之前，增加</span> <span class="n">placeHolder</span>
	<span class="sr">//</span> <span class="err">如果有</span> <span class="n">delay</span> <span class="err">延迟标记</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">options</span> <span class="o">&amp;</span> <span class="no">SDWebImageDelayPlaceholder</span><span class="p">))</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">创建主线程异步队列来更新</span> <span class="no">UI</span>
        <span class="n">dispatch_main_async_safe</span><span class="p">(</span><span class="o">^</span><span class="p">{</span>
            <span class="nb">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">placeholder</span><span class="p">;</span>
        <span class="p">});</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">判断</span> <span class="n">url</span> <span class="err">是否为空</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="p">{</span>
        <span class="sr">//</span> <span class="err">是否展示</span> <span class="no">ActivityIndicator</span> 
        <span class="k">if</span> <span class="p">(</span><span class="o">[</span><span class="nb">self</span> <span class="n">showActivityIndicatorView</span><span class="o">]</span><span class="p">)</span> <span class="p">{</span>
            <span class="o">[</span><span class="nb">self</span> <span class="n">addActivityIndicator</span><span class="o">]</span><span class="p">;</span>
        <span class="p">}</span>
		<span class="sr">//</span> <span class="err">防止</span> <span class="n">block</span> <span class="err">的</span> <span class="n">retain</span> <span class="n">cycle</span><span class="err">，进行弱引用转换</span>
        <span class="n">__weak</span> <span class="n">__typeof</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span><span class="n">wself</span> <span class="o">=</span> <span class="nb">self</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">使用图片</span> <span class="n">download</span> <span class="err">方法，并完成</span> <span class="n">callback</span> <span class="err">回调</span>
        <span class="nb">id</span> <span class="o">&lt;</span><span class="no">SDWebImageOperation</span><span class="o">&gt;</span> <span class="n">operation</span> <span class="o">=</span> <span class="o">[</span><span class="no">SDWebImageManager</span><span class="o">.</span><span class="n">sharedManager</span> <span class="ss">downloadImageWithURL</span><span class="p">:</span><span class="n">url</span> <span class="ss">options</span><span class="p">:</span><span class="n">options</span> <span class="ss">progress</span><span class="p">:</span><span class="n">progressBlock</span> <span class="ss">completed</span><span class="p">:</span><span class="o">^</span><span class="p">(</span><span class="no">UIImage</span> <span class="o">*</span><span class="n">image</span><span class="p">,</span> <span class="no">NSError</span> <span class="o">*</span><span class="n">error</span><span class="p">,</span> <span class="no">SDImageCacheType</span> <span class="n">cacheType</span><span class="p">,</span> <span class="no">BOOL</span> <span class="n">finished</span><span class="p">,</span> <span class="no">NSURL</span> <span class="o">*</span><span class="n">imageURL</span><span class="p">)</span> <span class="p">{</span>
	        <span class="sr">//</span> <span class="err">停止</span> <span class="no">ActivityIndicator</span> <span class="err">转动</span>
            <span class="o">[</span><span class="n">wself</span> <span class="n">removeActivityIndicator</span><span class="o">]</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">wself</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
            <span class="n">dispatch_main_sync_safe</span><span class="p">(</span><span class="o">^</span><span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">wself</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
                <span class="sr">//</span> <span class="err">图片是否使用了默认参数</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">image</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">options</span> <span class="o">&amp;</span> <span class="no">SDWebImageAvoidAutoSetImage</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">completedBlock</span><span class="p">)</span>
                <span class="p">{</span>
                	<span class="sr">//</span> <span class="err">【要点</span> <span class="mi">3</span><span class="err">】对</span> <span class="n">download</span> <span class="err">中的</span> <span class="n">completedBlock</span> <span class="err">成员传参</span>
                    <span class="n">completedBlock</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> <span class="n">cacheType</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span>
                    <span class="k">return</span><span class="p">;</span>
                <span class="p">}</span>
                <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">image</span><span class="p">)</span> <span class="p">{</span>
                	<span class="sr">//</span> <span class="err">更新图片，需要重新进行</span> <span class="n">layout</span> <span class="err">布局，主动调用</span> <span class="n">setNeedsLayout</span> <span class="err">方法</span>
                    <span class="n">wself</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span><span class="p">;</span>
                    <span class="o">[</span><span class="n">wself</span> <span class="n">setNeedsLayout</span><span class="o">]</span><span class="p">;</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="k">if</span> <span class="p">((</span><span class="n">options</span> <span class="o">&amp;</span> <span class="no">SDWebImageDelayPlaceholder</span><span class="p">))</span> <span class="p">{</span>
                        <span class="n">wself</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">placeholder</span><span class="p">;</span>
                        <span class="o">[</span><span class="n">wself</span> <span class="n">setNeedsLayout</span><span class="o">]</span><span class="p">;</span>
                    <span class="p">}</span>
                <span class="p">}</span> 
                <span class="sr">//</span> <span class="err">判断</span> <span class="n">finished</span> <span class="err">标记，传入</span> <span class="n">block</span> <span class="err">方法参数</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">completedBlock</span> <span class="o">&amp;&amp;</span> <span class="n">finished</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">completedBlock</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> <span class="n">cacheType</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">});</span>
        <span class="p">}</span><span class="o">]</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">将上面的</span> <span class="n">operation</span> <span class="err">添加到字典中，</span><span class="n">key</span> <span class="err">为</span> <span class="no">UIImageViewImageLoad</span>
        <span class="o">[</span><span class="nb">self</span> <span class="ss">sd_setImageLoadOperation</span><span class="p">:</span><span class="n">operation</span> <span class="ss">forKey</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;UIImageViewImageLoad&quot;</span><span class="o">]</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">处理</span> <span class="n">url</span> <span class="err">为</span> <span class="kp">nil</span> <span class="err">的状态</span>
    	<span class="sr">//</span> <span class="err">保证在主线程中处理，因为涉及到</span> <span class="no">UI</span>
        <span class="n">dispatch_main_async_safe</span><span class="p">(</span><span class="o">^</span><span class="p">{</span>
            <span class="o">[</span><span class="nb">self</span> <span class="n">removeActivityIndicator</span><span class="o">]</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">completedBlock</span><span class="p">)</span> <span class="p">{</span>
                <span class="no">NSError</span> <span class="o">*</span><span class="n">error</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSError</span> <span class="ss">errorWithDomain</span><span class="p">:</span><span class="no">SDWebImageErrorDomain</span> <span class="ss">code</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span> <span class="ss">userInfo</span><span class="p">:</span><span class="err">@</span><span class="p">{</span><span class="no">NSLocalizedDescriptionKey</span> <span class="p">:</span> <span class="err">@</span><span class="s2">&quot;Trying to load a nil url&quot;</span><span class="p">}</span><span class="o">]</span><span class="p">;</span>
                <span class="n">completedBlock</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> <span class="no">SDImageCacheTypeNone</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>下面来看深入看一下上述代码注释中的 3 个要点。</p>
<ul>
  <li>[self sd_cancelCurrentImageLoad]</li>
</ul>
<p>进入函数内层是以下代码：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="n">sd_cancelCurrentImageLoad</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">通过</span> <span class="n">key</span> <span class="err">来取消操作</span>
    <span class="o">[</span><span class="nb">self</span> <span class="ss">sd_cancelImageLoadOperationWithKey</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;UIImageViewImageLoad&quot;</span><span class="o">]</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">UIView</span><span class="o">+</span><span class="no">WebCacheOperation</span><span class="o">.</span><span class="n">m</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">sd_cancelImageLoadOperationWithKey</span><span class="p">:(</span><span class="no">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">key</span> <span class="p">{</span>
    <span class="sr">//</span> <span class="no">Cancel</span> <span class="k">in</span> <span class="n">progress</span> <span class="n">downloader</span> <span class="n">from</span> <span class="n">queue</span>
    <span class="sr">//</span> <span class="err">取消正在下载的队列</span>
    <span class="no">NSMutableDictionary</span> <span class="o">*</span><span class="n">operationDictionary</span> <span class="o">=</span> <span class="o">[</span><span class="nb">self</span> <span class="n">operationDictionary</span><span class="o">]</span><span class="p">;</span>
    <span class="nb">id</span> <span class="n">operations</span> <span class="o">=</span> <span class="o">[</span><span class="n">operationDictionary</span> <span class="ss">objectForKey</span><span class="p">:</span><span class="n">key</span><span class="o">]</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">如果</span> <span class="n">operationDictionary</span> <span class="err">可以取到</span> <span class="n">key</span><span class="err">，则可以得到与该视图相关的操作</span>
    <span class="sr">//</span> <span class="err">并根据</span> <span class="n">key</span> <span class="err">从字典中取消这些操作</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">operations</span><span class="p">)</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">检查</span> <span class="n">operations</span> <span class="err">是否为</span> <span class="n">array</span> <span class="err">，防止重名</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">[</span><span class="n">operations</span> <span class="ss">isKindOfClass</span><span class="p">:</span><span class="o">[</span><span class="no">NSArray</span> <span class="n">class</span><span class="o">]]</span><span class="p">)</span> <span class="p">{</span>
        	<span class="sr">//</span> <span class="err">比那里当中所有遵循给定代理的对象，对其下载任务进行取消</span>
            <span class="k">for</span> <span class="p">(</span><span class="nb">id</span> <span class="o">&lt;</span><span class="no">SDWebImageOperation</span><span class="o">&gt;</span> <span class="n">operation</span> <span class="k">in</span> <span class="n">operations</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">operation</span><span class="p">)</span> <span class="p">{</span>
                    <span class="o">[</span><span class="n">operation</span> <span class="n">cancel</span><span class="o">]</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="sr">//</span> <span class="err">如果不是集合，那么可能是一个下载对象</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">[</span><span class="n">operations</span> <span class="ss">conformsToProtocol</span><span class="p">:</span><span class="vi">@protocol</span><span class="p">(</span><span class="no">SDWebImageOperation</span><span class="p">)</span><span class="o">]</span><span class="p">){</span>
            <span class="o">[</span><span class="p">(</span><span class="nb">id</span><span class="o">&lt;</span><span class="no">SDWebImageOperation</span><span class="o">&gt;</span><span class="p">)</span> <span class="n">operations</span> <span class="n">cancel</span><span class="o">]</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="sr">//</span> <span class="err">所有元素已过滤，删除</span> <span class="n">key</span>
        <span class="o">[</span><span class="n">operationDictionary</span> <span class="ss">removeObjectForKey</span><span class="p">:</span><span class="n">key</span><span class="o">]</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>从代码中，可以看出：SD 使用 <code>NSDictionary</code> 来管理满足 <code>SDWebImageOperation</code> 代理的实例。通过对代理实例的判断，以及使用键值查询 operation 的方式，SD 可以有效、迅速的管理所有下载任务。</p>
<ul>
  <li>objc_setAssociatedObject(self, &amp;imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);</li>
</ul>
<p>这里是使用关联对象（Associated Object）（如果这里陌生，可以查阅 <em><a href="https://desgard.com/Associated-Objects/">浅谈Associated Objects</a></em> 这篇博文），来对 UIImageView 做了一个关联值。在第二个参数上，一般是关联对象的唯一标记，在 <em>UIImageView_WebCache.m</em> 中使用了一个静态量地址来作为这个 key。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">static</span> <span class="n">char</span> <span class="n">imageURLKey</span><span class="p">;</span></code></pre></div>
<p>增加这个 url 属性便于在其他地方迅速访问。在获取时候，只需要调用 <code>- (NSURL *)sd_imageURL</code> 便可以直接通过关联对象迅速查询。这个 url 的访问在其他地方会有多次调用。</p>
<ul>
  <li>completedBlock(image, error, cacheType, url);</li>
</ul>
<p>默认情况下，SD 会等 image 完全从网络下载完成之后，直接替换 UIImageView 中的 image 。如果想在获取图片之后，手动处理之后的所有事情，则需要设置此方法。</p>
<p>这个方法是将会在 <code>SDWebImageManager.h</code> 出现。</p>
<h2 id="setanimationimageswithurls-">setAnimationImagesWithURLs 图片组</h2>
<p>在这个 Category 中，还有对于动画组图的设置。观看源码可知，这个方法在实现上是将多个图片的 URL 打包成 array，传入 <code>sd_setImageLoadOperation</code> 方法来增加图片加载的 Operation 。而在打包中，相当于多次执行了 <code>sd_setImageWithURL</code> （其中的处理细节是一样的）。唯一不同的是，<code>setAnimationImagesWithURLs</code> 没有去设置关联对象。因为在展示中，我不需要对其做任何的控制，所以也就没有提供访问的快捷方法。</p>
<h2 id="uiimageviewwebcachem-">UIImageView+WebCache.m 源码解读总结</h2>
<p>对于常用的 <code>setImageWithURL</code> 方法，可以总结成以下流程：</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/UIImageView+WebCache.png" alt="UIImageView+WebCache" /></p>
<p>从最常用的 <code>setImageWithURL</code> 可以看出，其实 SDWebImage 的逻辑很清晰，其源码阅读起来可读性也很高。</p>
<p>在阅读三方库源码的同时，也可以感受到作者的代码经验所在。如同委托构造的方式，也是经验的积累总结。所以在学习代码的同时，也可以学习编码思想。</p>
<h2 id="section-1">延伸阅读</h2>
<p><a href="http://cocoadocs.org/docsets/SDWebImage/3.7.0/Categories/UIImageView+WebCache.html#//api/name/sd_setAnimationImagesWithURLs:">cocoadocs SDWebImage</a></p>
<p><a href="http://draveness.me/ios-yuan-dai-ma-jie-xi-sdwebimage/">iOS 源代码分析—-SDWebImage</a></p>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《SDWebImage Source Probe: WebCache》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/SDWebImage1/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/SDWebImage1/&amp;title=《SDWebImage Source Probe: WebCache》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/SDWebImage1/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/SDWebImage1/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>